/* eslint-disable no-console */
import { exec } from 'node:child_process'
import { promisify } from 'node:util';
import { basename, dirname, join } from 'node:path';
import { access, readdir, stat, unlink, writeFile } from 'node:fs/promises';
import { app } from 'electron';
import { existsAsync } from '../utils/fs'
import { normalizePath } from './utils';

const execAsync = promisify(exec)

export class ScriptManager {
  // 使用文件执行 PowerShell 脚本，避免命令行编码和转义问题
  private getAppsScriptPath = join(app.getPath('userData'), 'get-apps-from-registry.ps1')
  private getSystemAppsScriptPath = join(app.getPath('userData'), 'get-system-apps.ps1')

  public async extractIconWithPowerShell (
    exePath: string,
    outputPath: string,
  ): Promise<boolean> {
    if (process.platform !== 'win32') {
      return false;
    }
  
    try {
      // 转义路径中的特殊字符
      const escapedPath = exePath.replace(/\\/g, '\\\\').replace(/'/g, '\'\'');
      const escapedOutput = outputPath.replace(/\\/g, '\\\\').replace(/'/g, '\'\'');
      
      // 提取 icon 到 outputPath 脚本
      const powershellScript = `
        Add-Type -AssemblyName System.Drawing
        $icon = [System.Drawing.Icon]::ExtractAssociatedIcon('${escapedPath}')
        if ($icon) {
          $bitmap = $icon.ToBitmap()
          $bitmap.Save('${escapedOutput}', [System.Drawing.Imaging.ImageFormat]::Png)
          $icon.Dispose()
          $bitmap.Dispose()
          Write-Output 'SUCCESS'
        } else {
          Write-Output 'FAILED'
        }
      `;
  
      const encodedScript = Buffer.from(powershellScript, 'utf16le').toString('base64');
      const command = `powershell -NoProfile -ExecutionPolicy Bypass -EncodedCommand ${encodedScript}`;
      const { stdout } = await execAsync(command, {
        maxBuffer: 1024 * 1024,
        encoding: 'utf8',
      });
  
      return stdout.trim() === 'SUCCESS' && (await existsAsync(outputPath))
    } catch (error) {
      console.error('PowerShell extract icon error:', error);
      return false;
    }
  }

  public async getSystemApps (): Promise<InstalledApp[]> {
    if (process.platform !== 'win32') {
      return [];
    }

    const powershellScript = `
$ErrorActionPreference = 'Stop'
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
$apps = @()

# 获取系统应用和开始菜单应用
try {
  $startApps = Get-StartApps -ErrorAction SilentlyContinue
  foreach ($app in $startApps) {
    $appName = $app.Name
    $appId = $app.AppID
    
    # 过滤掉一些不需要的应用
    if ($appName -and $appId) {
      # 跳过一些系统更新和插件
      if ($appName -notmatch '^Update for|^KB\\d+|^Security Update') {
        # 尝试获取应用路径和图标
        $appPath = $null
        $appIcon = $null
        
        # 对于 UWP 应用，AppID 通常是 PackageFamilyName!ApplicationId
        if ($appId -match '^(.+)!(.+)$') {
          $packageFamilyName = $matches[1]
          try {
            # 尝试从 UWP 应用包获取信息
            $package = Get-AppxPackage | Where-Object { $_.PackageFamilyName -eq $packageFamilyName } | Select-Object -First 1
            if ($package) {
              $appPath = $package.InstallLocation
              # UWP 应用的图标通常在 Assets 目录
              $logoPath = Join-Path $package.InstallLocation 'Assets\\Logo.png'
              if (Test-Path $logoPath) {
                $appIcon = $logoPath
              }
            }
          } catch {}
        } else {
          # 对于传统应用，尝试从快捷方式获取路径
          try {
            $shell = New-Object -ComObject Shell.Application
            $folder = $shell.Namespace('shell:AppsFolder')
            $item = $folder.ParseName($appId)
            if ($item) {
              $appPath = $item.Path
              # 尝试获取图标
              $appIcon = $appPath
            }
          } catch {}
        }
        
        $appObj = @{
          name = $appName
          publisher = 'Microsoft'
          version = $null
          installDate = $null
          uninstallString = $null
          installLocation = $appPath
          icon = $appIcon
          displayIcon = $appIcon
          mainFilePath = $appPath
          appId = $appId
        }
        $apps += $appObj
      }
    }
  }
} catch {
  Write-Error "Get system apps error: $_"
}

# 添加一些常见的系统工具
$systemTools = @(
  @{ name = '控制面板'; appId = 'Microsoft.Windows.ControlPanel'; path = 'control.exe' },
  @{ name = '任务管理器'; appId = 'Microsoft.Windows.TaskManager'; path = 'taskmgr.exe' },
  @{ name = '系统设置'; appId = 'ms-settings:'; path = 'ms-settings:' },
  @{ name = '文件资源管理器'; appId = 'Microsoft.Windows.Explorer'; path = 'explorer.exe' },
  @{ name = '命令提示符'; appId = 'Microsoft.Windows.CommandPrompt'; path = 'cmd.exe' },
  @{ name = 'PowerShell'; appId = 'Microsoft.Windows.PowerShell'; path = 'powershell.exe' },
  @{ name = '注册表编辑器'; appId = 'Microsoft.Windows.RegistryEditor'; path = 'regedit.exe' },
  @{ name = '设备管理器'; appId = 'Microsoft.Windows.DeviceManager'; path = 'devmgmt.msc' },
  @{ name = '服务'; appId = 'Microsoft.Windows.Services'; path = 'services.msc' },
  @{ name = '事件查看器'; appId = 'Microsoft.Windows.EventViewer'; path = 'eventvwr.msc' }
)

foreach ($tool in $systemTools) {
  $systemPath = $null
  $installLocation = $null
  
  try {
    if ($tool.path -match '\\.(exe|msc)$') {
      # 查找系统路径
      $systemRoot = $env:SystemRoot
      $systemPaths = @(
        (Join-Path $systemRoot 'System32'),
        (Join-Path $systemRoot 'SysWOW64'),
        $systemRoot
      )
      foreach ($sysPath in $systemPaths) {
        $fullPath = Join-Path $sysPath $tool.path
        if (Test-Path $fullPath) {
          $systemPath = $fullPath
          $installLocation = Split-Path $systemPath -Parent
          break
        }
      }
    } else {
      # URI 类型（如 ms-settings:）
      $systemPath = $tool.path
      $installLocation = $null
      
      # 对于系统设置，尝试从 Windows 设置应用包获取图标
      if ($tool.appId -eq 'ms-settings:') {
        try {
          $settingsPackage = Get-AppxPackage | Where-Object { 
            $_.Name -like '*Settings*' -or 
            $_.PackageFamilyName -like '*Settings*' 
          } | Select-Object -First 1
          
          if ($settingsPackage -and $settingsPackage.InstallLocation) {
            $logoPath = Join-Path $settingsPackage.InstallLocation 'Assets\\Logo.png'
            if (Test-Path $logoPath) {
              $installLocation = $settingsPackage.InstallLocation
              $systemPath = $logoPath
            } else {
              # 尝试其他可能的图标路径
              $possibleLogoPaths = @(
                (Join-Path $settingsPackage.InstallLocation 'Assets\\Square44x44Logo.png'),
                (Join-Path $settingsPackage.InstallLocation 'Assets\\Square150x150Logo.png'),
                (Join-Path $settingsPackage.InstallLocation 'Assets\\StoreLogo.png')
              )
              foreach ($logo in $possibleLogoPaths) {
                if (Test-Path $logo) {
                  $installLocation = $settingsPackage.InstallLocation
                  $systemPath = $logo
                  break
                }
              }
            }
          }
        } catch {
          # 如果获取失败，继续使用 URI
        }
      }
    }
    
    if ($systemPath) {
      # 对于系统设置，mainFilePath 应该是 URI，icon 应该是图标文件路径
      if ($tool.appId -eq 'ms-settings:') {
        $iconPath = if ($installLocation) { $systemPath } else { $null }
        $appObj = @{
          name = $tool.name
          publisher = 'Microsoft'
          version = $null
          installDate = $null
          uninstallString = $null
          installLocation = $installLocation
          icon = $iconPath
          displayIcon = $iconPath
          mainFilePath = 'ms-settings:'
          appId = $tool.appId
        }
      } else {
        $appObj = @{
          name = $tool.name
          publisher = 'Microsoft'
          version = $null
          installDate = $null
          uninstallString = $null
          installLocation = $installLocation
          icon = $systemPath
          displayIcon = $systemPath
          mainFilePath = $systemPath
          appId = $tool.appId
        }
      }
      $apps += $appObj
    }
  } catch {
    Write-Error "Error processing system tool $($tool.name): $_"
  }
}

if ($apps.Count -eq 0) {
  Write-Output '[]'
} else {
  $apps | ConvertTo-Json -Depth 10 -Compress
}
`;

    try {
      // 将脚本写入文件，避免编码和命令行长度限制问题
      // 使用 UTF-8 with BOM 确保 PowerShell 正确识别中文字符
      if (!(await existsAsync(this.getSystemAppsScriptPath))) {
        const BOM = '\uFEFF';
        await writeFile(this.getSystemAppsScriptPath, BOM + powershellScript, 'utf-8');
      }
      
      // 执行 PowerShell 脚本文件，使用 -File 参数而不是 -EncodedCommand
      // 使用 -Encoding UTF8 确保正确读取 UTF-8 with BOM 文件
      const command = `powershell -NoProfile -ExecutionPolicy Bypass -File "${this.getSystemAppsScriptPath}"`;
      const { stdout, stderr } = await execAsync(
        command,
        {
          maxBuffer: 10 * 1024 * 1024,
          encoding: 'utf8',
        },
      );

      if (stderr && stderr.trim()) {
        console.error('PowerShell error output:', stderr);
      }

      let cleanedOutput = stdout.trim();
      if (!cleanedOutput) {
        console.error('getSystemApps: PowerShell output is empty');
        return [];
      }

      if (cleanedOutput.length > 0 && cleanedOutput.charCodeAt(0) === 0xFEFF) {
        cleanedOutput = cleanedOutput.slice(1);
      }

      let apps = JSON.parse(cleanedOutput) as InstalledApp[];
      if (!Array.isArray(apps)) {
        console.error('parse result is not an array:', apps);
        return [];
      }
      
      apps = apps.filter(app => {
        return ['控制面板', '任务管理器', '系统设置'].includes(app.name)
      })

      // 规范化路径
      for (const app of apps) {
        if (app.installLocation) {
          app.installLocation = normalizePath(app.installLocation);
        }
        if (app.displayIcon) {
          app.displayIcon = normalizePath(app.displayIcon);
        }
        app.mainFilePath = await this.parseMainFilePath(app)
      }

      console.info(`[getSystemApps] 成功获取 ${apps.length} 个系统应用`);
      if (apps.length > 0) {
        const systemToolNames = apps.map(a => a.name);
        console.log('[getSystemApps] 系统工具:', systemToolNames);
      }
      return apps;
    } catch (error: any) {
      console.error('get system apps error:', error);
      if (error.stderr) {
        console.error('PowerShell stderr:', error.stderr);
      }
      if (error.stdout) {
        console.error('PowerShell stdout:', error.stdout);
      }
      return [];
    }
  }

  public async getInstalledAppsFromRegistry (): Promise<InstalledApp[]> {
    if (process.platform !== 'win32') {
      return [];
    }
  
    const powershellScript = `
$ErrorActionPreference = 'Stop'
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
$apps = @()
$registryPaths = @(
  'HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*',
  'HKLM:\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*',
  'HKCU:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*'
)

foreach ($path in $registryPaths) {
  $items = Get-ItemProperty $path -ErrorAction SilentlyContinue
  if ($items) {
    foreach ($item in $items) {
      $name = $item.DisplayName
      if ($name -and $name -notmatch '^Update for|^KB\\d+|^Security Update|^Microsoft.*Update') {
        $displayIcon = $item.DisplayIcon
        $app = @{
          name = $name
          publisher = $item.Publisher
          version = $item.DisplayVersion
          installDate = $item.InstallDate
          uninstallString = $item.UninstallString
          installLocation = $item.InstallLocation
          icon = $displayIcon
          displayIcon = $displayIcon
        }
        $apps += $app
      }
    }
  }
}

$scoopRoots = @(
  "$env:USERPROFILE\\scoop\\apps",
  "$env:ProgramData\\scoop\\apps"
) | Where-Object { $_ -and (Test-Path $_) }

foreach ($root in $scoopRoots) {
  foreach ($appDir in Get-ChildItem -Path $root -Directory -ErrorAction SilentlyContinue) {
    $currentPath = Join-Path $appDir.FullName 'current'
    if (-not (Test-Path $currentPath)) {
      continue
    }

    $manifestPath = Join-Path $currentPath 'manifest.json'
    $manifest = $null
    if (Test-Path $manifestPath) {
      try {
        $manifest = Get-Content $manifestPath -Raw | ConvertFrom-Json
      } catch {}
    }

    $exe = Get-ChildItem -Path $currentPath -Filter '*.exe' -File -ErrorAction SilentlyContinue | Select-Object -First 1
    $exePath = if ($exe) { $exe.FullName } else { $null }

    $appName = if ($manifest -and $manifest.name) { $manifest.name } else { $appDir.Name }
    $appVersion = if ($manifest -and $manifest.version) { $manifest.version } else { $null }
    $publisher = if ($manifest -and $manifest.homepage) { $manifest.homepage } else { $null }

    $app = @{
      name = $appName
      publisher = $publisher
      version = $appVersion
      installDate = $null
      uninstallString = $exePath
      installLocation = $currentPath
      icon = $exePath
      displayIcon = $exePath
    }
    $apps += $app
  }
}

if ($apps.Count -eq 0) {
  Write-Output '[]'
} else {
  $apps | ConvertTo-Json -Depth 10 -Compress
}
`;
    
    try {
      if (!(await existsAsync(this.getAppsScriptPath))) {
        await writeFile(this.getAppsScriptPath, powershellScript, 'utf-8');
      }
      
      // 执行 PowerShell 脚本文件，使用 -File 参数而不是 -Command
      // 这样可以避免编码和转义问题
      const command = `powershell -NoProfile -ExecutionPolicy Bypass -File "${this.getAppsScriptPath}"`;
      const { stdout, stderr } = await execAsync(
        command,
        { 
          maxBuffer: 10 * 1024 * 1024, // 10MB buffer
          encoding: 'utf8', // 确保输出使用 UTF-8 编码
        },
      );
      
      // 检查是否有错误输出
      if (stderr && stderr.trim()) {
        console.error('PowerShell error output:', stderr);
      }
      
      // 清理输出：去除 BOM、空白字符等
      let cleanedOutput = stdout.trim();
      
      // 检查输出是否为空
      if (!cleanedOutput) {
        return [];
      }
      
      // 去除可能的 UTF-8 BOM
      if (cleanedOutput.length > 0 && cleanedOutput.charCodeAt(0) === 0xFEFF) {
        cleanedOutput = cleanedOutput.slice(1);
      }
      
      // 尝试解析 JSON
      let apps = JSON.parse(cleanedOutput) as InstalledApp[];
      // 确保 apps 是数组
      if (!Array.isArray(apps)) {
        console.error('parse result is not an array:', apps);
        return [];
      }

      apps = apps.filter(app => {
        return app.displayIcon || app.icon || app.installLocation
      })
  
      const deniedNameKeywords = [
        'update',
        'kb',
        'security update',
        'plugin',
        'steam',
        'wegame',
        'dellsupport',
      ]
      const microsoftWhitelist = ['visual studio code']

      // 排除一些软件
      apps = apps.filter(app => {
        const name = app.name.toLowerCase()
        const uninstall = app.uninstallString?.toLowerCase() ?? ''

        const isDeniedName = deniedNameKeywords.some(keyword =>
          name.includes(keyword),
        )
        const isDeniedUninstall = deniedNameKeywords.some(keyword =>
          uninstall.includes(keyword),
        )
        const isFtools = name === 'ftools'
        const isMicrosoftApp =
          name.includes('microsoft') || uninstall.includes('microsoft')
        const isWhitelistedMicrosoft = microsoftWhitelist.some(keyword =>
          name.includes(keyword),
        )

        return (
          !isDeniedName
          && !isDeniedUninstall
          && !isFtools
          && (!isMicrosoftApp || isWhitelistedMicrosoft)
        )
      })
  
      for (const app of apps) {
        if (app.uninstallString) {
          app.uninstallString = normalizePath(app.uninstallString)
        }
        if (app.uninstallString && !app.uninstallString.endsWith('.exe')) {
          const index = app.uninstallString.indexOf('.exe')
          app.uninstallString = app.uninstallString.slice(0, index + 4)
        }
        if (app.installLocation) {
          app.installLocation = normalizePath(app.installLocation)
        }
        if (app.displayIcon) {
          app.displayIcon = normalizePath(app.displayIcon)
        }
        if (!app.installLocation && app.uninstallString) {
          app.installLocation = dirname(app.uninstallString)
        }
        app.mainFilePath = await this.parseMainFilePath(app)
      }
      
      // 去重（按名称）
      const uniqueApps = Array.from(
        new Map(apps.map(app => [app.name, app])).values(),
      );
      
      return uniqueApps.sort((a, b) => a.name.localeCompare(b.name));
    } catch (error) {
      console.error('get installed apps from registry error:', error);
      return [];
    }
  }

  public async deleteGetAppsScriptFile () {
    try {
      if (await existsAsync(this.getAppsScriptPath)) {
        await unlink(this.getAppsScriptPath);
      }
      if (await existsAsync(this.getSystemAppsScriptPath)) {
        await unlink(this.getSystemAppsScriptPath);
      }
    } catch (e) {
      console.error('clear temp script file error:', e);
    }
  }

  private async parseMainFilePath (app: InstalledApp) {
    // 如果 mainFilePath 已经是 URI 类型（如 ms-settings:），直接返回
    if (app.mainFilePath) {
      const isUri = /^[a-z][a-z0-9+.-]*:/.test(app.mainFilePath) && 
        !app.mainFilePath.includes('\\') && 
        !app.mainFilePath.match(/^[a-z]:/i); // 排除 Windows 盘符路径（如 C:\）
      if (isUri) {
        return app.mainFilePath;
      }
    }
    
    if (
      app.displayIcon
      && app.displayIcon.includes('.exe')
      && !basename(app.displayIcon).toLowerCase().includes('unins')
    ) {
      if (!app.displayIcon.endsWith('.exe')) {
        const index = app.displayIcon.indexOf('.exe')
        return app.displayIcon.slice(0, index + 4)
      } else {
        return app.displayIcon
      }
    }
    if (app.installLocation && app.name) {
      try {
        const path = join(app.installLocation, `${app.name}.exe`)
        await access(path)
        return path
      } catch {}
    }
    if (app.installLocation) {
      try {
        const path = join(app.installLocation, `${basename(app.installLocation)}.exe`)
        await access(path)
        return path
      } catch {}
  
      try {
        const status = await stat(app.installLocation)
        if (status.isDirectory()) {
          const files = (await readdir(app.installLocation))
            .filter(file => {
              if (!file.endsWith('.exe')) {
                return false
              }
              
              if (app.uninstallString) {
                const uninstallFile = basename(app.uninstallString)
                return file !== uninstallFile
              }
              return !file.includes('unins')
            })
      
          if (files.length > 0) {
            return join(app.installLocation, files[0])
          }
        }
      } catch {}
    }
    return ''
  }
}

export const scriptManager = new ScriptManager()
